package com.pivotal.cloud.mybatis.autoconfigure;

import org.apache.ibatis.io.VFS;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @className: com.pivotal.cloud.mybatis.autoconfigure.SpringBootVFS
 * @projectName: PivotalCloud项目
 * @module: PivotalCloud项目-SpringBootVFS类，主要位于Mybatis模块-SpringBootVFS拓展支持模块
 * @content: SpringBootVFS-SpringBootVFS拓展支持
 * @author: Powered by Marklin
 * @datetime: 2025-11-06 01:40
 * @version: 1.0.0
 * @copyright: Copyright © 2018-2025 PivotalCloud Systems Incorporated. All rights
 * reserved.
 */
public class SpringBootVFS extends VFS {

	private static Charset urlDecodingCharset;

	private static Supplier<ClassLoader> classLoaderSupplier;

	static {
		setUrlDecodingCharset(Charset.defaultCharset());
		setClassLoaderSupplier(ClassUtils::getDefaultClassLoader);
	}

	private final ResourcePatternResolver resourceResolver;

	public SpringBootVFS() {
		this.resourceResolver = new PathMatchingResourcePatternResolver(classLoaderSupplier.get());
	}

	/**
	 * Set the charset for decoding an encoded URL string.
	 * <p>
	 * Default is system default charset.
	 * </p>
	 * @param charset the charset for decoding an encoded URL string
	 *
	 * @since 2.3.0
	 */
	public static void setUrlDecodingCharset(Charset charset) {
		urlDecodingCharset = charset;
	}

	/**
	 * Set the supplier for providing {@link ClassLoader} to used.
	 * <p>
	 * Default is a returned instance from {@link ClassUtils#getDefaultClassLoader()}.
	 * </p>
	 * @param supplier the supplier for providing {@link ClassLoader} to used
	 *
	 * @since 3.0.2
	 */
	public static void setClassLoaderSupplier(Supplier<ClassLoader> supplier) {
		classLoaderSupplier = supplier;
	}

	private static String preserveSubpackageName(final String baseUrlString, final Resource resource,
			final String rootPath) {
		try {
			return rootPath + (rootPath.endsWith("/") ? "" : "/")
					+ Normalizer.normalize(URLDecoder.decode(resource.getURL().toString(), urlDecodingCharset.name()),
							Normalizer.Form.NFC).substring(baseUrlString.length());
		}
		catch (IOException e) {
			throw new UncheckedIOException(e);
		}
	}

	@Override
	public boolean isValid() {
		return true;
	}

	@Override
	protected List<String> list(URL url, String path) throws IOException {
		String urlString = URLDecoder.decode(url.toString(), urlDecodingCharset.name());
		String baseUrlString = urlString.endsWith("/") ? urlString : urlString.concat("/");
		Resource[] resources = resourceResolver.getResources(baseUrlString + "**/*.class");
		return Stream.of(resources).map(resource -> preserveSubpackageName(baseUrlString, resource, path))
				.collect(Collectors.toList());
	}

}
